/*
 * @Author: xiaosihan
 * @Date: 2021-05-11 23:21:24
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-11-23 11:13:14
 */

import _ from "lodash";
import * as THREE from "three";
import { LineMaterial } from "three/examples/jsm/lines/LineMaterial.js";
import { LineSegments2 as THREELineSegments2 } from "three/examples/jsm/lines/LineSegments2.js";
import { LineSegmentsGeometry } from "three/examples/jsm/lines/LineSegmentsGeometry.js";
import { LinePoints } from "../Line/Line";
import Object3D from "../Object3D/Object3D";
import Object3DComponent, { Object3DComponentProps } from "../Object3D/Object3DComponent";
import { Object3DEvent } from "../Renderer/type";
import Transition from "../xrt_util/Transition";

export interface LineSegments2Props extends Object3DComponentProps {
    points?: LinePoints,
    color?: string[], // 顶点颜色
    opacity?: number,// 透明度
    dashSize?: number, // 虚线长度
    gapSize?: number, // 虚线间隔
    dashOffset?: number, // 虚线位移调整
    dashSpeed?: number, // 虚线流动速度
    linewidth?: number, // 线宽
    fog?: boolean,
}

interface State { }

export default class LineSegments2<P = {}> extends Object3DComponent<P & LineSegments2Props, State> {
    constructor(props: P & LineSegments2Props) {
        super(props);

        let name = this.getConstructorName();

        this.object3d = new (Function("THREELineSegments2", `
            return class ${name} extends THREELineSegments2 {
            };
        `)(THREELineSegments2));

    }

    declare object3d: THREELineSegments2 & Object3D;

    // 线条模型
    lineGeometry!: LineSegmentsGeometry & { _maxInstanceCount: number };

    // 线条材质
    lineMaterial!: LineMaterial;

    // 带有缓动效果的线顶点对象
    TLineVector3 = new Transition();

    // 时钟对象
    Clock = new THREE.Clock();

    //颜色过渡对象
    Tcolor = new Transition();

    // 线条的缓动参数
    TLineParam = new Transition();

    componentDidMount() {

        this.lineGeometry = this.object3d.geometry as (LineSegmentsGeometry & { _maxInstanceCount: number });

        // 初始一个正确的结构
        this.lineGeometry.setPositions([0, 0, 0, 0, 0, 0]);

        this.lineMaterial = this.object3d.material;

        this.lineGeometry.setAttribute("color", new THREE.Float32BufferAttribute([0, 0, 0], 3));

        this.lineGeometry.instanceCount = 0;

        // 开启虚线
        this.lineMaterial.dashed = true;

        this.lineMaterial.color.set("#fff");

        this.setLineMaterial();
        this.setLinGeometry();
        this.setVectorColor();

        this.object3d.addEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
    }

    componentDidUpdate() {
        this.setLineMaterial();
        this.setLinGeometry();
        this.setVectorColor();

        this.object3d.removeEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
        this.object3d.addEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
    }

    componentWillUnmount() {
        this.object3d.material.dispose()
        this.object3d.geometry.dispose();
        this.object3d.removeEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
    }

    // 设置线模型
    setLinGeometry() {

        let { points = [], bezier, duration } = this.props;

        // 保证点数为双数
        if (points.length % 2 === 1) {
            points.length = points.length - 1;
        }

        const { lineGeometry } = this;

        let newPoints = _.flatten(points);// 把传入的参数转成顶点格式 一维数组

        this.TLineVector3.setBezier(bezier).setDuration(duration).set(newPoints);

        // 长度不相等时 重新创建 线条position顶点对象
        if (lineGeometry.instanceCount < (points.length / 2)) {

            let originPoints = this.TLineVector3.getJson();

            for (let i in newPoints) {

                if (!_.isUndefined(originPoints[i])) {
                    newPoints[i] = originPoints[i];
                } else {
                    continue;
                }
            }

            lineGeometry.setPositions(newPoints);

            this.object3d.computeLineDistances(); // 虚线计算
        }

        lineGeometry.instanceCount = points.length / 2;
        lineGeometry._maxInstanceCount = points.length / 2;
    }

    // 设置线材质
    setLineMaterial() {
        const {
            bezier,
            duration,
            opacity = 1,
            dashSize = 1,
            gapSize = 0,
            dashOffset = 0,
            fog = false,
            linewidth = 1
        } = this.props;

        this.TLineParam
            .setBezier(bezier)
            .setDuration(duration)
            .set({ opacity, dashSize, gapSize, linewidth });

        // FIXME three没有这个参数了 需要想其他办法支持了
        Object.assign(this.lineMaterial, {
            // dashOffset,
            dashed: true,
            vertexColors: true,
            transparent: true,
            fog
        });

    }

    static ColorTemp = new THREE.Color();

    // 设置顶点颜色
    setVectorColor() {

        let { bezier, duration = 500, color = ["#fff"] } = this.props;

        const { lineGeometry } = this;

        let newVectorColor: number[] = [];

        (_.isEmpty(color) ? ["#fff"] : color).map(c => {
            const { r, g, b } = LineSegments2.ColorTemp.set(c);
            newVectorColor.push(r, g, b);
        });

        // 长度不相等时 重新创建 线条position顶点对象
        if (lineGeometry.attributes.color.array.length < lineGeometry.attributes.instanceStart.array.length) {
            let newColor = new THREE.Float32BufferAttribute(lineGeometry.attributes.instanceStart.array.length + 3, 3);
            lineGeometry.setAttribute("color", newColor);
        }

        this.Tcolor.setBezier(bezier).setDuration(duration).set(newVectorColor);
    }

    // 虚线流动计算
    componentBeforeRender(renderer: THREE.WebGLRenderer, scene: THREE.Scene, camera: THREE.Camera) {
        const { lineMaterial } = this;
        const { dashSpeed = 0 } = this.props;
        renderer.getSize(lineMaterial.resolution);
        // FIXME
        // lineMaterial.dashOffset -= dashSpeed * this.Clock.getDelta();
        return true;
    }


    setTransformVector3Position = () => {

        const { lineGeometry, lineMaterial } = this;

        let keepRun = false;

        // 设置每个顶点的位置
        if (!this.TLineVector3.isDone()) {

            let instanceStart = lineGeometry.attributes.instanceStart;

            let count = lineGeometry.instanceCount * 2;

            for (let i = 0; i <= count; i++) {
                instanceStart.setXYZ(
                    i / 2,
                    this.TLineVector3.get(i * 3),
                    this.TLineVector3.get((i * 3) + 1),
                    this.TLineVector3.get((i * 3) + 2)
                );
            }

            instanceStart.needsUpdate = true; // 更新顶点位置

            keepRun = true;
        }

        // 设置材质上面的参数
        if (!this.TLineParam.isDone()) {

            const { opacity, dashSize, gapSize, linewidth } = this.TLineParam.getJson();

            Object.assign(lineMaterial, {
                opacity, // 透明度
                dashSize, // 虚线长度
                gapSize, // 虚线间隔
                linewidth, // 虚线间隔
            });

            lineMaterial.needsUpdate = true;
            keepRun = true;
        }

        if (keepRun) {
            this.object3d.computeLineDistances(); // 虚线计算
        }

        if (!this.Tcolor.isDone()) {

            let colorArray = Object.values(this.Tcolor.getJson()) as Array<number>;

            let lineColor = lineGeometry.attributes.color;

            // 设置线条顶点的颜色
            for (let i = 0; i < lineColor.array.length; i += colorArray.length) {

                if (lineColor.array.length - colorArray.length >= i) {
                    (lineColor.array as Float32Array).set(colorArray, i);
                } else {
                    (lineColor.array as Float32Array).set(colorArray.slice(0, lineColor.array.length % colorArray.length), i);
                }

            }

            // @ts-ignore
            lineGeometry.setColors(lineColor.array);

            keepRun = true;
        }

        if (!keepRun) {
            this.object3d.removeEventListener(Object3DEvent.BEFORE_RENDER, this.setTransformVector3Position);
        }

    }


}